use std::rc::Rc;

use crate::interfaces;
use crate::interfaces::TaskInterface::TaskInterface;
use crate::interfaces::PhaseInterface::PhaseInterface;
use crate::interfaces::Logger::LogInterface;


pub struct Workflow {
    pub _phases: Vec<Rc<dyn PhaseInterface>>,
    pub _signature: &'static str,
    pub _logger: Box<dyn LogInterface>,
    pub _description: &'static str,
    pub _phase_done: Vec<Rc<dyn PhaseInterface>>,
    pub _current_phase: Option<usize>,
    pub _tasks_processed: Vec<Rc<dyn TaskInterface>>,
    pub _tasks_ready: Vec<Rc<dyn TaskInterface>>,
    pub _revert: bool,
}

impl Workflow {
    pub fn get_phase(&self, idx: usize) -> Option<&Rc<dyn PhaseInterface>> {
        return self.phases().get(idx);
    }

    pub fn start(&mut self) {
        self.log_info(&("Workflow triggered: ".to_string() + self.signature() + " ---- " + self.description()));

        match self.process_tasks() {
            Err((info, code)) => {
                if self._revert {
                    self.log_warning("============== Serializing running status.");
                    // todo: serialize and save current running state.

                    self.log_warning("============== trying to revert to avoid damage.");
                    self.revert_all_after_failure();
                }
                else {
                    self.log_warning("============== revert disabled, nothing to do.");
                }
            },
            Ok(_) => {},
        }
    }

    pub fn revert_all_after_failure(&self) {
        let mut finished_tasks = self._tasks_processed.clone();
        while let Some(task) = finished_tasks.pop() {
            self.log_warning(&("============== Reverting task: ".to_string() + task.signature()));
            task.revert();
        }

        let mut finsihed_phases = self._phase_done.clone();
        while let Some(phase) = finsihed_phases.pop() {
            self.log_warning(&("============== Reverting phase: ".to_string() + phase.signature()));
            phase.revert();
        }
    }

    pub fn process_tasks(&mut self) -> Result<(), (&'static str, i32)> {
        for idx in 0..self.phases().len() {
            self.set_current_phase(idx);
            
            self.log_info(&("=================== Processing Phase: ".to_string() +  self.get_phase(idx).expect("[Fatal] Phase index Error.").signature()));
            match self.process_by_phases() {
                Ok(_) => {
                    self._phase_done.push(Rc::clone(self.get_phase(self._current_phase.expect("[Fatal] Phase index Error.")).expect("[Fatal] Phase index Error.")));
                },
                Err((info, code)) => {
                    self.log_error(&("[".to_string() + &code.to_string() + "] Workflow halt at phase " + self.get_phase(idx).expect("[Fatal] Phase index Error.").signature() + " for: " + info));
                    return Err((info, code));
                }
            }
        }

        return Ok(());
    }

    pub fn set_current_phase(&mut self, idx: usize) {
        self._current_phase = if idx  < self.phases().len() {
            Some(idx)
        }
        else {
            None
        };
    }

    pub fn process_by_phases(&mut self) -> Result<(), (&'static str, i32)> {
        self.phase_enqueue();

        while let Some(task) = self._tasks_ready.pop() {
            self._tasks_processed.push(task.clone());
            match task.run() {
                Ok(()) => {
                    self.log_info(&("- [OK] Task ".to_string() + task.signature() + " done!"));
                },
                Err((info, code)) => {
                    self.log_error(&("[Fatal Error] Task ".to_string() + task.signature() + "  " + &code.to_string() + " with information " + info));
                    return Err((info, code));
                }
            }
        }

        Ok(())
    }

    pub fn phase_enqueue(&mut self) {
        assert_eq!(self._tasks_ready.len(), 0);
        // self._tasks_done.clear();
        self._tasks_processed.clear();

        match self._current_phase {
            Some(idx) => {
                for task in self._phases[idx].tasks() {
                    self._tasks_ready.push(Rc::clone(task));
                }
                self._tasks_ready.reverse();
            },
            None => {
                panic!("Unexpected phase index.");
            }
        }
    }

    pub fn log_debug(&self, s: &str) { self._logger.log_debug(s); }
    pub fn log_info(&self, s: &str) { self._logger.log_info(s); }
    pub fn log_warning(&self, s: &str) { self._logger.log_warning(s); }
    pub fn log_error(&self, s: &str) { self._logger.log_error(s); }

    pub fn signature(&self) -> &str { return self._signature; }
    pub fn description(&self) -> &str { return self._description; }

    fn phases(&self) -> &Vec<Rc<dyn PhaseInterface>> { return &self._phases; }
}
